﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Csla;
using Csla.Data;
using System.Data;
using System.Data.Common;
using Microsoft.Practices.EnterpriseLibrary.Data;
using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;

namespace Asah.SiteWorx.SiteMap
{
    [Serializable()]
    public abstract class NavigationMenu
    {
        private NavigationMenu()
        {
            // force the use of the factory method.
        }

        #region Factory Methods

        internal static LinkedList<PageInfo> GetPublicMenu()
        {
            PageList list = PageList.GetPageList();
            LinkedList<PageInfo> menuList = new LinkedList<PageInfo>();

            var result = from page in list
                         where page.Visibility == PageVisibility.Public &&
                             page.DisplayInMenu == true && page.ParentPageId == -1
                         orderby page.MenuPosition
                         select page;

            foreach (PageInfo info in result)
            {
                BuildChildPublicMenu(info, ref list);
                menuList.AddLast(info);
            }

            return menuList;
        }

        private static void BuildChildPublicMenu(PageInfo pageInfo, ref PageList list)
        {
            int parentPageId = pageInfo.ID;
            var result = from page in list
                         where page.Visibility == PageVisibility.Public &&
                             page.DisplayInMenu == true && page.ParentPageId == parentPageId
                         orderby page.MenuPosition
                         select page;

            foreach (PageInfo info in result)
            {
                BuildChildPublicMenu(info, ref list);
                pageInfo.ChildPages.AddLast(info);
            }
        }

        public static LinkedList<PageInfo> GetFullMenu()
        {
            PageList list = PageList.GetPageList();
            LinkedList<PageInfo> menuList = new LinkedList<PageInfo>();

            var result = from page in list
                         where page.DisplayInMenu == true && page.ParentPageId == -1
                         orderby page.MenuPosition
                         select page;

            foreach (PageInfo info in result)
            {
                BuildChildFullMenu(info, ref list);
                menuList.AddLast(info);
            }

            return menuList;
        }

        private static void BuildChildFullMenu(PageInfo pageInfo, ref PageList list)
        {
            int parentPageId = pageInfo.ID;
            var result = from page in list
                         where page.DisplayInMenu == true && page.ParentPageId == parentPageId
                         orderby page.MenuPosition
                         select page;

            foreach (PageInfo info in result)
            {
                BuildChildFullMenu(info, ref list);
                pageInfo.ChildPages.AddLast(info);
            }
        }

        public static List<PageInfo> GetFullNonMenu()
        {
            PageList list = PageList.GetPageList();
            List<PageInfo> menuList = new List<PageInfo>();

            var result = from page in list
                         where page.DisplayInMenu == false
                         orderby page.MenuText
                         select page;

            foreach (PageInfo info in result)
            {
                menuList.Add(info);
            }

            return menuList;
        }

        public static void UpdateMenu(LinkedList<PageInfo> menu)
        {
            DataPortal.Execute<UpdateMenuCommand>(new UpdateMenuCommand(ref menu));
            
            // after the menu is updated we need to refresh the site object.
            Site.GetSite().Refresh();
        }

        internal static void RemovePageFromMenu(int pageId)
        {
            LinkedList<PageInfo> menu = GetFullMenu();
            PageInfo menuItem = null;
            FindAndRemovePage(pageId,ref menuItem, menu);

            DataPortal.Execute<RemoveMenuCommand>(new RemoveMenuCommand(ref menuItem));
            DataPortal.Execute<UpdateMenuCommand>(new UpdateMenuCommand(ref menu));
        }

        private static void FindAndRemovePage(int sourcePageId,ref PageInfo sourcePageInfo, 
                                              LinkedList<PageInfo> pages)
        {
            foreach (PageInfo info in pages)
            {
                if (info.ID == sourcePageId) // found source page.
                {
                    sourcePageInfo = info;
                    pages.Remove(info);
                    return;
                }
                else
                {
                    FindAndRemovePage(sourcePageId,ref sourcePageInfo, info.ChildPages);
                }
            }
        }

        #endregion
        
        #region UpdateMenuCommand

        [Serializable()]
        private class UpdateMenuCommand : CommandBase
        {
            private LinkedList<PageInfo> _menu;

            public UpdateMenuCommand(ref LinkedList<PageInfo> menu)
            {
                _menu = menu;
            }

            protected override void DataPortal_Execute()
            {
                ProcessStructure(-1, _menu);
            }

            private void ProcessStructure(int parentPageId, LinkedList<PageInfo> menu)
            {
                Database db = DatabaseFactory.CreateDatabase("SiteWorx");
                DbCommand dbCommand;

                int index = 0;
                foreach (PageInfo info in menu)
                {
                    dbCommand = db.GetStoredProcCommand("aspnet_SiteMap_UpdateMenuStructure");
                    db.AddInParameter(dbCommand, "ApplicationId", DbType.Guid, Site.GetSite().ApplicationId);
                    db.AddInParameter(dbCommand, "PageId", DbType.Int32, info.ID);
                    db.AddInParameter(dbCommand, "DisplayInMenu", DbType.Boolean, true);
                    db.AddInParameter(dbCommand, "MenuPosition", DbType.Int32, index);
                    db.AddInParameter(dbCommand, "ParentPageId", DbType.Int32, parentPageId);

                    try
                    {
                        db.ExecuteNonQuery(dbCommand);
                        dbCommand.Dispose();
                    }
                    catch (Exception ex)
                    {
                        bool rethrow = ExceptionPolicy.HandleException(ex, "Data Access Layer Policy");
                        if (rethrow)
                        {
                            throw;
                        }
                    }

                    ProcessStructure(info.ID, info.ChildPages);
                    index++;
                }
            }
        }
        #endregion

        #region RemoveMenuCommand

        [Serializable()]
        private class RemoveMenuCommand : CommandBase
        {
            private PageInfo _menuItem;

            public RemoveMenuCommand(ref PageInfo menuItem)
            {
                _menuItem = menuItem;
            }

            protected override void DataPortal_Execute()
            {
                Database db = DatabaseFactory.CreateDatabase("SiteWorx");
                DbCommand dbCommand = db.GetStoredProcCommand("aspnet_SiteMap_UpdateMenuStructure");
                db.AddInParameter(dbCommand, "ApplicationId", DbType.Guid, Site.GetSite().ApplicationId);
                db.AddInParameter(dbCommand, "PageId", DbType.Int32, _menuItem.ID);
                db.AddInParameter(dbCommand, "DisplayInMenu", DbType.Boolean, false);
                db.AddInParameter(dbCommand, "MenuPosition", DbType.Int32, -1);
                db.AddInParameter(dbCommand, "ParentPageId", DbType.Int32, -1);

                try
                {
                    db.ExecuteNonQuery(dbCommand);
                    dbCommand.Dispose();
                }
                catch (Exception ex)
                {
                    bool rethrow = ExceptionPolicy.HandleException(ex, "Data Access Layer Policy");
                    if (rethrow)
                    {
                        throw;
                    }
                }

                ProcessStructure(_menuItem.ChildPages);
            }

            private void ProcessStructure(LinkedList<PageInfo> menu)
            {
                Database db = DatabaseFactory.CreateDatabase("SiteWorx");
                DbCommand dbCommand;

                foreach (PageInfo menuItem in menu)
                {
                    dbCommand = db.GetStoredProcCommand("aspnet_SiteMap_UpdateMenuStructure");
                    db.AddInParameter(dbCommand, "ApplicationId", DbType.Guid, Site.GetSite().ApplicationId);
                    db.AddInParameter(dbCommand, "PageId", DbType.Int32, menuItem.ID);
                    db.AddInParameter(dbCommand, "DisplayInMenu", DbType.Boolean, false);
                    db.AddInParameter(dbCommand, "MenuPosition", DbType.Int32, -1);
                    db.AddInParameter(dbCommand, "ParentPageId", DbType.Int32, -1);

                    try
                    {
                        db.ExecuteNonQuery(dbCommand);
                        dbCommand.Dispose();
                    }
                    catch (Exception ex)
                    {
                        bool rethrow = ExceptionPolicy.HandleException(ex, "Data Access Layer Policy");
                        if (rethrow)
                        {
                            throw;
                        }
                    }

                    ProcessStructure(menuItem.ChildPages);
                }
            }
        }
        #endregion
    }
}
